import numpy as np

from init import *
from sklearn.decomposition import PCA
from skfuzzy.cluster import cmeans
import copy
from collections import Counter
import geatpy as ea
from init import *
import pandas as pd

plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False


class MyProblem(ea.Problem):  # 继承Problem父类
    def __init__(self, data, index_length):
        name = 'MyProblem'  # 初始化name（函数名称，可以随意设置）
        M = 1  # 初始化M（目标维数）
        maxormins = [-1]  # 初始化maxormins（目标最小最大化标记列表，1：最小化该目标；-1：最大化该目标）
        Dim = index_length  # 初始化Dim（决策变量维数）
        varTypes = [1] * Dim  # 初始化varTypes（决策变量的类型，元素为0表示对应的变量是连续的；1表示是离散的）
        lb = [0] * Dim  # 决策变量下界
        ub = [255] * Dim  # 决策变量上界
        lbin = [1] * Dim  # 决策变量下边界（0表示不包含该变量的下边界，1表示包含）
        ubin = [1] * Dim  # 决策变量上边界（0表示不包含该变量的上边界，1表示包含）
        # 调用父类构造方法完成实例化
        ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin)
        self.left_index = data[0]
        self.right_index = data[1]
        self.Hs = data[2]

    def aimFunc(self, pop):  # 目标函数
        Vars = pop.Phen  # 得到决策变量矩阵
        x = Vars.astype(int)  # 得到决策变量矩阵
        # print("x", x.shape)
        # print(Vars.shape)
        # x1 = Vars[:, [0]]
        # print()
        left_indexs = self.left_index
        Hs = self.Hs
        right_indexs = self.right_index
        x = np.sort(x)
        LeftX = x[:, :left_indexs]
        rightX = x[:, left_indexs:]
        # print('leftindex,rightindex',left_indexs,right_indexs)
        row, col = x.shape
        capity = np.zeros(row)
        dist = np.zeros(row)
        # print('left',LeftX.shape)
        for i in range(row):
            P1 = LeftX[i, :]
            P2 = rightX[i, :]
            # tmp1 = np.unique(P1, return_counts=True)
            # tmp2 = np.unique(P2, return_counts=True)
            # # print('tmp1,tmp2',tmp2,tmp1)
            # is_def1 = np.sum(tmp1[1] != 1)
            # is_def2 = np.sum(tmp2[1] != 1)
            # # print('is_def2',is_def2,is_def1)
            # # is_def1 == 0 and is_def2 == 0
            # # print('is_def1', is_def1)
            # print('asd',(not Hs[P1].any(0)) and (np.sum(P1 < (255 / 2)) == len(P1)) and (not Hs[P2].any(0)) and (np.sum(P2 > (255 / 2)) == len(P2)))
            if (np.sum(Hs[P1] == 0) == 0) and (np.sum(Hs[P2] == 0) == 0):
                # P1 = getModifyMap(P1, 0)
                # P2 = getModifyMap(P2, 1)
                # print(123)
                for j in range(len(P1)):
                    capity[i] += Hs[P1[j]]
                    dist[i] += np.sum(Hs[:P1[j]])+Hs[P1[j]]/2
                for k in range(len(P2)):
                    capity[i] += Hs[P2[k]]
                    dist[i] += np.sum(Hs[P2[k]:])+Hs[P2[k]]/2
            else:
                dist[i] = 1000000000000

        # pop.CV = Hs[]
        opera = capity / dist
        pop.ObjV = np.vstack([opera]).T  # 计算目标函数值，赋值给pop种群对象的ObjV属性

    # def calReferObjV(self):  # 设定目标数参考值（本问题目标函数参考值设定为理论最优值）
    #     referenceObjV = np.array([[2.5]])
    #     return referenceObjV


def jijue(data, totalindex):
    """===============================实例化问题对象==========================="""
    problem = MyProblem(data, totalindex)  # 生成问题对象
    """=================================种群设置==============================="""
    Encoding = 'P'  # 编码方式
    NIND = 500  # 种群规模
    Field = ea.crtfld(Encoding, problem.varTypes, problem.ranges, problem.borders)  # 创建区域描述器
    population = ea.Population(Encoding, Field, NIND)  # 实例化种群对象（此时种群还没被初始化，仅仅是完成种群对象的实例化）
    """===============================算法参数设置============================="""
    myAlgorithm = ea.soea_SEGA_templet(problem, population)  # 实例化一个算法模板对象
    myAlgorithm.mutOper.Pm = 0.1  # 修改变异算子的变异概率
    myAlgorithm.recOper.XOVR = 0.3  # 修改交叉算子的交叉概率
    myAlgorithm.MAXGEN = 1000  # 最大进化代数
    myAlgorithm.verbose = False  # 设置是否打印输出日志信息
    myAlgorithm.drawing = 0  # 设置绘图方式（0：不绘图；1：绘制结果图；2：绘制目标空间过程动画；3：绘制决策空间过程动画）
    """==========================调用算法模板进行种群进化======================="""
    [NDSet, population] = myAlgorithm.run()  # 执行算法模板，得到非支配种群以及最后一代种群
    # NDSet.save()  # 把非支配种群的信息保存到文件中
    """==================================输出结果=============================="""
    print('用时：%s 秒' % myAlgorithm.passTime)
    print('最有个体', NDSet.ObjV)
    print('值', NDSet.Phen)
    print('非支配个体数：%d 个' % NDSet.sizes)
    # print(NDSet.Phen[0]*P)
    if NDSet.sizes != 0:
        print(NDSet.Phen[0])
        print(NDSet.ObjV)
        # print('P',data[0])
        # Px = NDSet.Phen[0] * data[0]
        # Zx = NDSet.Phen[0] * data[1]
        # Px = Px[Px != 0]
        # Zx = Zx[Zx != 0]
        # data = [Px, Zx, data[2]]
        # print('此时的参数', Px)
        return NDSet.Phen[0], NDSet.ObjV,NDSet
    else:
        print('没有找到可行解！')
        return data, 100000000


def img_hs(img_data):
    row, col = img_data.shape
    Hs = np.zeros(256)
    for i in range(row):
        for j in range(col):
            Hs[img_data[i][j]] += 1
    return Hs


def getModifyMap(P, types):
    P = np.sort(P)
    if types == 0:
        for i in range(len(P) - 1):
            P[i] -= len(P) - 1 - i
    if types == 1:
        for i in range(1, len(P)):
            P[i] += i
    return P


def his_make(data):
    x = np.arange(256)
    y = data
    # plt.plot(x, y, '-r', label='psnr')
    # plt.plot(x, y, '.b')
    plt.title('Lena')
    plt.xlabel('点大小', color='#1C2833')
    plt.ylabel('数量', color='#1C2833')
    plt.legend(loc='upper left')
    plt.grid()
    plt.show()


def img_shift(img_data, bins, leftindex, secret):
    row, col = img_data.shape
    Left = bins[0:leftindex]
    Right = bins[leftindex:]
    print('left', Left)
    print('right', Right)
    Left = getModifyMap(Left, 0)
    Right = getModifyMap(Right, 1)
    print('left2', Left)
    print('right2', Right)
    for k in range(len(Left) - 1, -1, -1):
        for i1 in range(row):
            for j in range(col):
                if img_data[i1][j] < Left[k]:
                    img_data[i1][j] -= 1
                if img_data[i1][j] == Left[k]:
                    img_data[i1][j] -= secret[i1][j]
    for k2 in range(len(Right)):
        for i2 in range(row):
            for j2 in range(col):
                if img_data[i2][j2] > Right[k2]:
                    img_data[i2][j2] += 1
                if img_data[i2][j2] == Right[k2]:
                    img_data[i2][j2] += secret[i2][j2]
    return img_data,Left,Right


def img_shifttest(img_data):
    row, col = img_data.shape

    for i in range(row):
        for j in range(col):
            if img_data[i][j] == 220:
                img_data[i][j] += 1
    return img_data


def secret_msg(row, col):
    '''

    :param ec: 需要嵌入的容量
    :return: 生成随机生成的二进制
    '''
    arr = np.arange(row * col)
    np.random.shuffle(arr)
    secret_msg = np.zeros((row, col))
    for i in range(row):
        for j in range(col):
            # secret_msg[i, j] = np.mod(arr[col*(i+1)-j-1],2)
            secret_msg[i, j] = np.mod(arr[col * (i + 1) - j - 1], 2)
    return secret_msg


def distion(Bins,left_index,newhs):
    Left = Bins[0:left_index]
    Right = Bins[left_index:]
    Left = getModifyMap(Left, 0)
    Right = getModifyMap(Right, 1)
    capity = 0
    dist = 0
    for j in range(len(Left)):
        capity += newhs[Left[j]]
        dist += np.sum(newhs[:Left[j]]) + newhs[Left[j]] / 2
    for k in range(len(Right)):
        capity += newhs[Right[k]]
        dist += np.sum(newhs[Right[k]:]) + newhs[Right[k]] / 2
    return capity,dist


def Ca_img(imgs):
    row,col = imgs.shape
    maxsize = 0
    minsize = 256
    hist = np.zeros(row*col)
    index = 0
    for i in range(row):
        for j in range(col):
            hist[index] = imgs[i][j]
            if imgs[i][j]<minsize:
                minsize = imgs[i][j]
            if imgs[i][j]>maxsize:
                maxsize = imgs[i][j]
            index+=1
    return hist,maxsize,minsize


def plt_make2(data,x,y,type):
    plt.hist(data, bins=255, color='steelblue',align='mid',rwidth=0.8)
    plt.title('Lena')
    plt.xlabel('Pixels', color='#1C2833')
    plt.ylabel('Occurence frequency', color='#1C2833')
    # plt.figure(figsize=(15, 5), dpi=80)
    if type == 0:
        plt.bar(x, y, align='center',color='red')
    plt.show()



if __name__ == '__main__':
    Img = Load_img('test')
    img = Img[0]
    imgs = copy.deepcopy(img)
    hist, maxsize, minsize  =Ca_img(imgs)
    # plt_make2(hist)
    imghs = img_hs(imgs)

    plt_make2(hist, 1, 2, 0)

    left_index = minsize
    right_index = 255 -maxsize
    total_index = minsize+254-maxsize
    data = [left_index, right_index, imghs]
    Bins, aa,NDSet = jijue(data, total_index)
    Bins = np.sort(Bins)
    ec = np.sum(imghs[Bins])
    count_number = imghs[Bins]
    secret = secret_msg(512, 512)
    plt_make2(hist,Bins,count_number,0)
    plt_make2(hist, Bins, count_number, 1)
    # plt_make3(Bins,count_number)
    newimg,Left,Right = img_shift(imgs, Bins, left_index, secret)
    newimghs = img_hs(newimg)
    hist2, maxsize, minsize = Ca_img(newimg)
    Bins2 = np.append(Left-1,Right+1)
    count_number2 = newimghs[Bins2]
    plt_make2(hist2,Bins2,count_number2,1)
    psnr = cal_psnr(img, newimg, 0)
    ecs,dist =distion(Bins,left_index,newimghs)

